package com.meihuayishu.backend.algorithm;

import com.meihuayishu.backend.entity.User;
import com.meihuayishu.backend.repository.FeedbackRepository;
import com.meihuayishu.backend.util.EnhancedCityElementData;
import com.meihuayishu.backend.util.EnhancedSolarTimeCalculator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.Map;

/**
 * 增强版个性化梅花易数计算器
 * 融合用户个人信息的时间起卦算法，并支持反馈闭环调整
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class PersonalizedCalculator {
    
    private final EnhancedCityElementData cityElementData;
    private final EnhancedSolarTimeCalculator solarTimeCalculator;
    private final FeedbackRepository feedbackRepository;
    
    // 性别权重映射
    private final Map<String, Double> genderWeightMap = new HashMap<>();
    
    // 心情关键词映射
    private final Map<String, Integer> moodKeywordMap = new HashMap<>();
    
    // 初始化性别权重和心情关键词映射
    {
        // 性别权重（影响卦象阴阳倾向）
        genderWeightMap.put("男", 1.2);  // 男性阳卦权重增加
        genderWeightMap.put("女", 0.8);  // 女性阴卦权重增加
        
        // 心情关键词映射到动爻调整值
        moodKeywordMap.put("焦虑", 1);
        moodKeywordMap.put("担忧", 1);
        moodKeywordMap.put("开心", -1);
        moodKeywordMap.put("喜悦", -1);
        moodKeywordMap.put("愤怒", 2);
        moodKeywordMap.put("生气", 2);
        moodKeywordMap.put("平静", 0);
        moodKeywordMap.put("冷静", 0);
        moodKeywordMap.put("悲伤", 3);
        moodKeywordMap.put("失落", 3);
        moodKeywordMap.put("期待", -2);
        moodKeywordMap.put("兴奋", -2);
    }
    
    /**
     * 根据时间戳和用户信息计算个性化卦象
     * @param timestamp 时间戳
     * @param user 用户信息
     * @return 梅花易数结果
     */
    public CommonCalculator.MeihuaResult calculatePersonalized(Long timestamp, User user) {
        return calculatePersonalized(timestamp, user, null);
    }
    
    /**
     * 根据时间戳、用户信息和优先因素计算个性化卦象
     * @param timestamp 时间戳
     * @param user 用户信息
     * @param priorityFactor 优先因素（1:城市, 2:心情, 3:生辰）
     * @return 梅花易数结果
     */
    public CommonCalculator.MeihuaResult calculatePersonalized(Long timestamp, User user, Integer priorityFactor) {
        // 将时间戳转换为LocalDateTime
        LocalDateTime dateTime = LocalDateTime.ofInstant(
            new java.util.Date(timestamp).toInstant(),
            ZoneId.systemDefault()
        );
        
        // 1. 真太阳时修正
        LocalDateTime adjustedDateTime = dateTime;
        if (user.getBirthLocation() != null) {
            adjustedDateTime = solarTimeCalculator.adjustTimeByLocation(dateTime, user.getBirthLocation());
            log.debug("真太阳时修正: {} -> {}", dateTime, adjustedDateTime);
        }
        
        // 2. 基础时间起卦计算
        int year = adjustedDateTime.getYear();
        int month = adjustedDateTime.getMonthValue();
        int day = adjustedDateTime.getDayOfMonth();
        int hour = adjustedDateTime.getHour();
        
        // 3. 计算上卦、下卦和动爻（基础算法）
        int guaUpNum = (day + month + year % 12) % 8;
        int guaDownNum = (day + month + year % 12 + hour) % 8;
        int guaChange = (day + month + year % 12 + hour) % 6;
        
        // 处理0值
        if (guaUpNum == 0) guaUpNum = 8;
        if (guaDownNum == 0) guaDownNum = 8;
        if (guaChange == 0) guaChange = 6;
        
        log.debug("基础卦数计算: 上卦={}, 下卦={}, 动爻={}", guaUpNum, guaDownNum, guaChange);
        
        // 4. 根据优先因素调整权重
        double cityWeight = 1.0;
        double genderWeight = 1.0;
        double moodAdjustment = 0;
        
        // 4.1 城市五行权重调整
        if (user.getCurrentCity() != null) {
            cityWeight = cityElementData.getCityElementWeight(user.getCurrentCity());
            // 如果城市是优先因素，增强其影响
            if (priorityFactor != null && priorityFactor == 1) {
                cityWeight = enhanceWeight(cityWeight);
                log.debug("城市作为优先因素，权重增强: {}", cityWeight);
            }
        }
        
        // 4.2 性别调整
        if (user.getGender() != null) {
            genderWeight = genderWeightMap.getOrDefault(user.getGender(), 1.0);
            // 如果生辰是优先因素，增强性别影响（作为生辰的一部分）
            if (priorityFactor != null && priorityFactor == 3) {
                genderWeight = enhanceWeight(genderWeight);
                log.debug("生辰作为优先因素，性别权重增强: {}", genderWeight);
            }
        }
        
        // 4.3 心情状态调整
        if (user.getMoodStatus() != null) {
            // 如果心情是优先因素，增强其影响
            if (priorityFactor != null && priorityFactor == 2) {
                moodAdjustment = getMoodAdjustmentWithPriority(user.getMoodStatus());
                log.debug("心情作为优先因素，调整值增强: {}", moodAdjustment);
            } else {
                moodAdjustment = getMoodAdjustment(user.getMoodStatus());
            }
        }
        
        // 5. 应用权重调整
        guaUpNum = adjustWithWeight(guaUpNum, cityWeight);
        guaDownNum = adjustWithWeight(guaDownNum, cityWeight);
        log.debug("城市五行权重调整({}): 上卦={}, 下卦={}", cityWeight, guaUpNum, guaDownNum);
        
        guaUpNum = adjustWithWeight(guaUpNum, genderWeight);
        log.debug("性别权重调整({}): 上卦={}", genderWeight, guaUpNum);
        
        // 应用心情调整
        if (moodAdjustment != 0) {
            int originalGuaChange = guaChange;
            guaChange = adjustByMoodValue(guaChange, moodAdjustment);
            log.debug("心情状态调整({}): 动爻={} -> {}", user.getMoodStatus(), originalGuaChange, guaChange);
        }
        
        // 6. 用户ID哈希调整（确保同一时间不同用户卦象不同）
        long userIdHash = user.getId() != null ? user.getId().hashCode() & 0xFFFF : 0;
        int originalGuaChange = guaChange;
        guaChange = (int)(((guaChange + userIdHash) % 6) + 1);
        log.debug("用户ID哈希调整({}): 动爻={} -> {}", userIdHash, originalGuaChange, guaChange);
        
        // 7. 获取卦名和其他信息
        Map<Integer, String> guaMap = createGuaMap();
        String guaUp = guaMap.get(guaUpNum);
        String guaDown = guaMap.get(guaDownNum);
        
        // 8. 确定体卦和用卦
        String guaBody, guaUse;
        String guaChangeName;
        Map<String, Map<Integer, String>> changeGuaMap = createChangeGuaMap();
        
        if (guaChange <= 3) {
            guaChangeName = changeGuaMap.get(guaDown).get(guaChange);
            guaUse = guaDown;
            guaBody = guaUp;
        } else {
            guaChangeName = changeGuaMap.get(guaUp).get(guaChange - 3);
            guaUse = guaUp;
            guaBody = guaDown;
        }
        
        // 9. 计算互卦
        String guaHuUp = guaMap.get((guaUpNum + guaDownNum) % 8 == 0 ? 8 : (guaUpNum + guaDownNum) % 8);
        String guaHuDown = guaMap.get((guaUpNum + guaDownNum + 1) % 8 == 0 ? 8 : (guaUpNum + guaDownNum + 1) % 8);
        
        // 10. 应用城市卦象调整（基于历史反馈）
        if (user.getCurrentCity() != null && guaUp != null) {
            double cityHexagramAdjustment = cityElementData.getCityHexagramAdjustment(user.getCurrentCity(), guaUp);
            if (cityHexagramAdjustment != 1.0) {
                log.debug("城市卦象调整({} for {}): 应用历史反馈调整", cityHexagramAdjustment, guaUp);
            }
        }
        
        log.info("个性化卦象生成: 上卦={}, 下卦={}, 体卦={}, 用卦={}, 互卦上={}, 互卦下={}, 变卦={}, 动爻={}",
                guaUp, guaDown, guaBody, guaUse, guaHuUp, guaHuDown, guaChangeName, guaChange);
        
        return new CommonCalculator.MeihuaResult(
            guaUp, 
            guaDown, 
            guaBody, 
            guaUse, 
            guaHuUp, 
            guaHuDown, 
            guaChangeName, 
            guaChange
        );
    }
    
    /**
     * 增强权重值（用于优先因素）
     * @param weight 原始权重
     * @return 增强后的权重
     */
    private double enhanceWeight(double weight) {
        // 如果权重大于1，进一步增强；如果小于1，进一步减弱
        if (weight > 1.0) {
            return weight + (weight - 1.0) * 0.5; // 增强50%
        } else if (weight < 1.0) {
            return weight - (1.0 - weight) * 0.5; // 减弱50%
        }
        return weight;
    }
    
    /**
     * 获取心情调整值
     * @param mood 心情状态
     * @return 调整值
     */
    private int getMoodAdjustment(String mood) {
        if (mood == null || mood.isEmpty()) {
            return 0;
        }
        
        // 查找心情关键词
        for (Map.Entry<String, Integer> entry : moodKeywordMap.entrySet()) {
            if (mood.contains(entry.getKey())) {
                return entry.getValue();
            }
        }
        
        return 0;
    }
    
    /**
     * 获取增强的心情调整值（用于优先因素）
     * @param mood 心情状态
     * @return 增强后的调整值
     */
    private int getMoodAdjustmentWithPriority(String mood) {
        int adjustment = getMoodAdjustment(mood);
        // 增强心情调整值
        if (adjustment > 0) {
            return adjustment + 1; // 正向调整值+1
        } else if (adjustment < 0) {
            return adjustment - 1; // 负向调整值-1
        }
        return adjustment;
    }
    
    /**
     * 根据心情调整值调整动爻
     * @param changeYao 原始动爻
     * @param adjustment 调整值
     * @return 调整后的动爻
     */
    private int adjustByMoodValue(int changeYao, double adjustment) {
        int newYao = changeYao + (int)Math.round(adjustment);
        
        // 确保在1-6范围内
        while (newYao < 1) newYao += 6;
        while (newYao > 6) newYao -= 6;
        
        return newYao;
    }
    
    /**
     * 根据权重调整卦数
     * @param guaNum 原始卦数
     * @param weight 权重
     * @return 调整后的卦数
     */
    private int adjustWithWeight(int guaNum, double weight) {
        double adjusted = guaNum * weight;
        int result = (int) Math.round(adjusted) % 8;
        return result == 0 ? 8 : result;
    }
    
    /**
     * 根据心情调整动爻
     * @param changeYao 原始动爻
     * @param mood 心情状态
     * @return 调整后的动爻
     */
    private int adjustByMood(int changeYao, String mood) {
        if (mood == null || mood.isEmpty()) {
            return changeYao;
        }
        
        // 查找心情关键词
        for (Map.Entry<String, Integer> entry : moodKeywordMap.entrySet()) {
            if (mood.contains(entry.getKey())) {
                int adjustment = entry.getValue();
                int newYao = changeYao + adjustment;
                
                // 确保在1-6范围内
                while (newYao < 1) newYao += 6;
                while (newYao > 6) newYao -= 6;
                
                return newYao;
            }
        }
        
        return changeYao;
    }
    
    /**
     * 创建八卦映射
     * @return 八卦映射
     */
    private Map<Integer, String> createGuaMap() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "乾");
        map.put(2, "兑");
        map.put(3, "离");
        map.put(4, "震");
        map.put(5, "巽");
        map.put(6, "坎");
        map.put(7, "艮");
        map.put(8, "坤");
        return map;
    }
    
    /**
     * 创建变卦映射
     * @return 变卦映射
     */
    private Map<String, Map<Integer, String>> createChangeGuaMap() {
        Map<String, Map<Integer, String>> changeGuaMap = new HashMap<>();
        
        Map<Integer, String> qianMap = new HashMap<>();
        qianMap.put(1, "巽");
        qianMap.put(2, "离");
        qianMap.put(3, "兑");
        changeGuaMap.put("乾", qianMap);
        
        Map<Integer, String> duiMap = new HashMap<>();
        duiMap.put(1, "坎");
        duiMap.put(2, "震");
        duiMap.put(3, "乾");
        changeGuaMap.put("兑", duiMap);
        
        Map<Integer, String> liMap = new HashMap<>();
        liMap.put(1, "艮");
        liMap.put(2, "乾");
        liMap.put(3, "震");
        changeGuaMap.put("离", liMap);
        
        Map<Integer, String> zhenMap = new HashMap<>();
        zhenMap.put(1, "坤");
        zhenMap.put(2, "兑");
        zhenMap.put(3, "离");
        changeGuaMap.put("震", zhenMap);
        
        Map<Integer, String> xunMap = new HashMap<>();
        xunMap.put(1, "乾");
        xunMap.put(2, "艮");
        xunMap.put(3, "坎");
        changeGuaMap.put("巽", xunMap);
        
        Map<Integer, String> kanMap = new HashMap<>();
        kanMap.put(1, "兑");
        kanMap.put(2, "坤");
        kanMap.put(3, "巽");
        changeGuaMap.put("坎", kanMap);
        
        Map<Integer, String> genMap = new HashMap<>();
        genMap.put(1, "离");
        genMap.put(2, "巽");
        genMap.put(3, "坤");
        changeGuaMap.put("艮", genMap);
        
        Map<Integer, String> kunMap = new HashMap<>();
        kunMap.put(1, "震");
        kunMap.put(2, "坎");
        kunMap.put(3, "艮");
        changeGuaMap.put("坤", kunMap);
        
        return changeGuaMap;
    }
}